# -*- coding: utf-8 -*-
import datetime
import random
import re
import subprocess
import time
import logging
import argparse

from appium import webdriver
# from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import StaleElementReferenceException

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
logger = logging

APP = "XHS"

app_package_map = {
    "XHS": ("com.xingin.xhs", ".index.v2.IndexActivityV2")
}


class XHSAppiumOperate:
    def __init__(self, **kwargs):

        self.screen_width = None
        self.screen_height = None

        self.deviceName = kwargs.get("deviceName")
        self.appium_url = kwargs.get("appium_url")
        self.platformVersion = kwargs.get("platformVersion")
        self.systemPort = kwargs.get("systemPort")
        self.adbPort = kwargs.get("adbPort")
        # 获取群聊名称列表
        self.group_names_str = kwargs.get('groupNames', '')
        self.time_interval_minutes = kwargs.get('time_interval_minutes', '')

        self.driver = None
        self.max_retries = 3
        self.failure_count = 0

    def run(self):
        while self.max_retries > 0:
            try:
                if self.driver:
                    self.driver.quit()
                self.connection()
                logger.info(f"设备 {self.deviceName} 连接成功")
                time.sleep(3)

                logger.info("开始执行小红书【设置作品权限为仅自己可见】任务...")
                self.script()
                break
            except Exception as e:
                logger.error(f"发生异常: {e}")
                self.failure_count += 1
                self.max_retries -= 1
                if self.max_retries == 0:
                    raise
        logger.info("任务结束")

    @staticmethod
    def execute_adb_command(adb_command):
        """
        执行单条 ADB 命令

        :param adb_command: 完整的 ADB 命令字符串
        :return: 命令执行结果（stdout）
        """
        try:
            logger.info(f"执行 ADB 命令: {adb_command}")
            result = subprocess.run(
                adb_command,
                shell=True,
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )
            logger.info(f"命令执行成功: {adb_command}")
            return result.stdout.decode('utf-8')
        except subprocess.CalledProcessError as e:
            logger.error(f"命令执行失败: {adb_command}")
            logger.error(f"错误信息: {e.stderr.decode('utf-8')}")

    def initialize_device_settings(self):
        """
        执行设备初始化所需的 ADB 命令
        """
        logger.info("开始初始化设备设置")

        commands = [
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global adb_keep_usb_enabled 1",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global miui_optimization 0",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global tcp_keepalive 1",
            f"adb -s {self.deviceName} -P {self.adbPort} shell dumpsys battery unplug",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm enable io.appium.uiautomator2.server",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm enable io.appium.uiautomator2.server.test",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global background_process_limit 5",
            f"adb -s {self.deviceName} -P {self.adbPort} shell am set-standby-bucket io.appium.uiautomator2.server ACTIVE",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm trim-caches 500M",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm clear io.appium.uiautomator2.server",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm clear io.appium.uiautomator2.server.test"
        ]

        for cmd in commands:
            try:
                self.execute_adb_command(cmd)
            except Exception as e:
                logger.error(f"执行命令失败: {cmd}")
                logger.error(f"错误详情: {e}")

    def connection(self):
        self.initialize_device_settings()
        desired_caps = {
            "platformName": "Android",
            # "udid": "FMR0223A31045650",
            "udid": self.deviceName,
            "platformVersion": self.platformVersion,
            "deviceName": self.deviceName,
            "automationName": "UiAutomator2",
            "appPackage": app_package_map[APP][0],
            "appActivity": app_package_map[APP][1],
            "noReset": True,
            "fullReset": False,
            "systemPort": int(self.systemPort),
            "adbPort": int(self.adbPort),
            "appWaitForLaunch": True,
            "noSign": True,
            "autoLaunch": True,
            "newCommandTimeout": 180,
            "skipLogcatCapture": True,
            "disableWindowAnimation": True,
            "disableAndroidWatchers": True,
            "adbExecTimeout": 30000,
            "uiautomator2ServerInstallTimeout": 60000,
            "skipDeviceInitialization": True,
        }

        # self.driver = webdriver.Remote(self.appium_url, options=UiAutomator2Options().load_capabilities(desired_caps))
        self.driver = webdriver.Remote(self.appium_url, desired_caps)
        self.screen_width, self.screen_height = self.get_window_size()
        self.wait_time = 2

    def script(self):
        logger.info("开始执行【扫描群聊】任务")

        self.find_element_with_retry(AppiumBy.XPATH, '//*[@text="消息"]').click()
        time.sleep(5)

        group_names = [name.strip() for name in self.group_names_str.split('\n') if name.strip()]

        logger.info(f"需要扫描的群聊列表: {group_names}")

        for group_name in group_names:
            try:
                logger.info(f"正在查找群聊: {group_name}")
                # 查找并点击群聊
                group_element = self.find_element_with_retry(AppiumBy.XPATH,
                                                             f'//android.view.ViewGroup[contains(@content-desc, "{group_name}")]')
                group_name = group_element.text
                logger.warning(f"已找到群聊: {group_name}")
                if group_element:
                    group_element.click()
                    time.sleep(3)

                    # 收集聊天记录
                    self.collect_chat_messages(group_name)

                    # 返回消息列表
                    self.driver.back()
                    time.sleep(2)
                else:
                    logger.warning(f"未找到群聊: {group_name}")
            except Exception as e:
                logger.error(f"处理群聊 {group_name} 时出错: {e}")
                # 出错时尝试返回消息列表页面
                try:
                    self.driver.back()
                    time.sleep(2)
                except:
                    pass

        self.normal_end()

    def collect_chat_messages(self, group_name):
        """
        收集群聊中的消息内容
        """
        collected_texts = []
        scroll_count = 0
        max_scrolls = 50  # 最大滚动次数，防止无限循环
        no_new_content_count = 0  # 连续没有新内容的次数

        logger.info(f"开始收集群聊 {group_name} 的消息")

        while scroll_count < max_scrolls and no_new_content_count < 2:
            try:
                # 获取当前页面的所有文本元素
                text_elements = self.driver.find_elements(AppiumBy.CLASS_NAME, "android.widget.TextView")
                current_texts = [elem.text.strip() for elem in text_elements if elem.text.strip()]

                # 检查是否有新内容
                new_texts = [text for text in current_texts if text not in collected_texts][5:]
                #  有一个间隔时间的参数，判断一下这里面的日期数据（如：10-14 12:40),如果采样到当前时间-间隔时间的数据，说明只采样这期间的new texts，可以跳出循环了

                if new_texts:
                    # 将新内容添加到收集列表的前面（因为是向上滚动）
                    collected_texts = new_texts + collected_texts
                    # logger.info(new_texts)
                    logger.info(f"本次滚动收集到 {len(new_texts)} 条新消息")
                    # 检查是否需要停止采集
                    if self.should_stop_collecting(collected_texts, self.time_interval_minutes):
                        logger.info("已采集到指定时间范围内的消息，停止采集")
                        break

                    no_new_content_count = 0  # 重置计数器
                else:
                    no_new_content_count += 1
                    logger.info("本次滚动未发现新内容")

                # 向上滚动
                self.swipe_to_next(0.2, 0.8)
                scroll_count += 1
                time.sleep(2)

            except StaleElementReferenceException:
                # 页面元素已过期，重新获取
                logger.warning("页面元素已过期，继续滚动")
                self.swipe_to_next(0.2, 0.8)
                scroll_count += 1
                time.sleep(2)
            except Exception as e:
                logger.error(f"收集消息时出错: {e}")
                break

        logger.info(f"群聊 {group_name} 共收集到 {len(collected_texts)} 条消息")
        # 这里可以保存收集到的消息到文件或数据库
        # 例如: self.save_messages(group_name, collected_texts)

    def should_stop_collecting(self, collected_texts, time_interval_minutes):
        """
        判断是否应该停止采集消息
        根据消息中的时间戳判断是否已经采集到足够近的消息
        """
        if not collected_texts:
            return False

        # 当前时间
        now = datetime.datetime.now()

        # 计算目标时间（当前时间减去间隔时间）
        target_time = now - datetime.timedelta(days=time_interval_minutes)

        # 查找最近的时间戳
        for text in collected_texts:
            # 匹配时间格式如：10-14 12:40 或 10月14日 12:40
            time_patterns = [
                r'(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2})',
                r'(\d{1,2})月(\d{1,2})日 (\d{1,2}):(\d{1,2})'
            ]

            for pattern in time_patterns:
                match = re.search(pattern, text)
                if match:
                    try:
                        # 解析时间
                        if pattern == r'(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2})':
                            month, day, hour, minute = map(int, match.groups())
                            msg_time = datetime.datetime(now.year, month, day, hour, minute)
                        else:
                            month, day, hour, minute = map(int, match.groups())
                            msg_time = datetime.datetime(now.year, month, day, hour, minute)

                        # 如果消息时间在目标时间之后，说明已经采集到足够近的消息
                        if msg_time >= target_time:
                            return True
                    except Exception as e:
                        logger.debug(f"解析时间失败: {e}")
                        continue

        return False

    def swipe_to_next(self, start_y, end_y):
        # 向上滑动
        self.driver.swipe(
            self.screen_width / 2,
            self.screen_height * start_y,
            self.screen_width / 2,
            self.screen_height * end_y,
            800
        )

    def find_element_with_retry(self, by, value, timeout=5):
        max_retries = 3
        for attempt in range(max_retries + 1):
            try:
                element = WebDriverWait(self.driver, timeout).until(
                    EC.presence_of_element_located((by, value))
                )
                return element
            except Exception as e:
                if attempt < max_retries:
                    logger.error(f"获取元素失败，重试")
                    time.sleep(1)  # 等待1秒后重试
                else:
                    logger.error(f"获取元素失败，超过3次")
                    return None

    def get_window_size(self):
        size = self.driver.get_window_size()
        return size['width'], size['height']

    def normal_end(self):
        logger.info("返回首页并退出")
        self.driver.press_keycode(3)  # HOME 键
        self.driver.quit()


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-j', '--job_id', dest='job_id', type=str, default=None, required=True, help='target job_id')
    parser.add_argument('-u', '--appium_url', dest='appium_url', type=str, default=None, help='target appium_url')
    parser.add_argument('-d', '--deviceName', dest='deviceName', type=str, default=None, help='target deviceName')
    parser.add_argument('-s', '--systemPort', dest='systemPort', type=str, default=None, help='target systemPort')
    parser.add_argument('-a', '--adbPort', dest='adbPort', type=str, default=None, help='target adbPort')
    parser.add_argument('-p', '--platformName', dest='platformName', type=str, default=None, help='target platformName')
    parser.add_argument('-ver', '--platformVersion', dest='platformVersion', type=str, default=None,
                        help='target platformVersion')
    parser.add_argument('-accountId', '--accountId', dest='accountId', type=str, default="000", help='target accountId')
    parser.add_argument('-v', '--variables', dest='variables', type=str, default='{}', help='target variables')
    parser.add_argument('-groupNames', '--groupNames', dest='groupNames', type=str, default='{}',
                        help='群名称唯一关键字')
    parser.add_argument('-time_interval_minutes', '--time_interval_minutes', dest='time_interval_minutes', type=int,
                        default='{}',
                        help='时间间隔')

    args = parser.parse_args()
    XHSAppiumOperate(**vars(args)).run()
